﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using Antlr.Runtime;
using Genuilder.Extensibility;

namespace Genuilder.Extensions.XamlVerifier
{
	public class XamlVerifierNode
	{
		public XamlVerifierNode(Location location)
		{
			_Location = location;
		}
		private readonly Location _Location;
		public Location Location
		{
			get
			{
				return _Location;
			}
		}

		internal virtual IEnumerable<XamlVerifierError> Visit(TypeResolver resolver, XamlVerifierContext context)
		{
			return null;
		}
	}
	public class StartVerify : XamlVerifierNode
	{
		public StartVerify(string type, Location location, Location idLocation, int index)
			: base(location)
		{
			_Type = type;
			_IdLocation = idLocation;
			_Index = index;
		}

		private readonly int _Index;
		public int Index
		{
			get
			{
				return _Index;
			}
		}
		private readonly Location _IdLocation;
		public Location IdLocation
		{
			get
			{
				return _IdLocation;
			}
		}
		private readonly string _Type;
		public string Type
		{
			get
			{
				return _Type;
			}
		}
		internal override IEnumerable<XamlVerifierError> Visit(TypeResolver resolver, XamlVerifierContext context)
		{
			var result = resolver.FindType(Type);
			context.Types.Push(Type);
			if(!result.Success)
				yield return new XamlVerifierTypeError()
				{
					Suggestion = result.Suggestion,
					TypeName = context.Types.Peek(),
					Location = IdLocation,
					Index = Index
				};
		}
		public override string ToString()
		{
			return "Start Verify : " + Type;
		}
	}
	public class EndVerify : XamlVerifierNode
	{
		public EndVerify(Location location)
			: base(location)
		{

		}
		internal override IEnumerable<XamlVerifierError> Visit(TypeResolver resolver, XamlVerifierContext context)
		{
			if(context.Types.Count > 0)
				context.Types.Pop();
			yield break;
		}
		public override string ToString()
		{
			return "End Verify";
		}
	}

	public class XamlBinding : XamlVerifierNode
	{
		public XamlBinding(string path, Location location, Location idLocation, int index)
			: base(location)
		{
			_Path = path;
			_IdLocation = idLocation;
			_Paths = path.Split('.');
			_Index = index;
		}

		private readonly int _Index;
		public int Index
		{
			get
			{
				return _Index;
			}
		}

		private readonly Location _IdLocation;
		public Location IdLocation
		{
			get
			{
				return _IdLocation;
			}
		}

		private readonly string _Path;
		public string Path
		{
			get
			{
				return _Path;
			}
		}

		public readonly string[] _Paths;
		public string[] Paths
		{
			get
			{
				return _Paths;
			}
		}

		internal override IEnumerable<XamlVerifierError> Visit(TypeResolver resolver, XamlVerifierContext context)
		{
			if(context.Types.Count == 0)
				yield break;


			var type = context.Types.Peek();
			int index = Index;
			for(int i = 0; i < Paths.Length; i++)
			{
				if(type == null)
					yield break;
				var result = resolver.FindProperty(type, Paths[i]);
				if(!result.Success)
				{
					yield return new XamlVerifierPropertyError()
					{
						PropertyName = Paths[i],
						Suggestion = result.Suggestion,
						ClassName = type,
						Location = IdLocation + new Location(0, i == 0 ? 0 : Paths[i - 1].Length + 1),
						Index = index
					};
				}
				type = result.TypeName;
				index += Paths[i].Length + 1;
			}
		}

		public override string ToString()
		{
			return "{Binding " + Path + "}";
		}
	}
	public class XamlVerifierReader
	{
		public XamlVerifierReader(Stream stream)
			: this(new StreamReader(stream))
		{
		}

		public XamlVerifierReader(string content)
			: this(new StringReader(content))
		{
		}


		XamlVerifierLexer _XamlLexer;
		public XamlVerifierReader(TextReader reader)
		{
			_XamlLexer = new XamlVerifierLexer(new ANTLRStringStream(reader.ReadToEnd()));
		}


		public IEnumerable<XamlVerifierNode> ReadAll()
		{
			while(true)
			{
				var node = Read();
				if(node == null)
					yield break;
				yield return node;
			}
		}
		public XamlVerifierNode Read()
		{
			var token = _XamlLexer.NextToken();
			if(token.Type == XamlVerifierLexer.EOF)
				return null;

			var location = new Location(token.Line, token.CharPositionInLine + 1);

			if(token.Type == XamlVerifierLexer.STARTVERIFY)
			{
				return new StartVerify(_XamlLexer.id.Text, location, _XamlLexer.idLocation, _XamlLexer.index);
			}
			else if(token.Type == XamlVerifierLexer.ENDVERIFY)
			{
				return new EndVerify(location);
			}
			else if(token.Type == XamlVerifierLexer.BINDING)
			{
				return new XamlBinding(_XamlLexer.path.Text, location, _XamlLexer.idLocation, _XamlLexer.index);
			}
			else
				throw new NotSupportedException("Not supported token at " + location.ToString());
		}
	}
}
